package com.microservice.auth.service.impl;

import com.alibaba.fastjson2.JSON;
import com.microservice.api.system.RemoteSystemService;
import com.microservice.auth.model.LoginUser;
import com.microservice.auth.service.ILoginService;
import com.microservice.core.constant.CacheConstants;
import com.microservice.core.constant.SecurityConstants;
import com.microservice.core.domain.AjaxResult;
import com.microservice.core.redis.service.RedisService;
import com.microservice.core.utils.BCryptUtil;
import com.microservice.core.utils.JwtUtil;
import com.microservice.core.utils.ip.IpUtils;
import com.microservice.core.utils.uuid.IdUtils;
import com.microservice.model.system.User;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 登录专属服务
 */
@Service
public class LoginServiceImpl implements ILoginService {

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private RemoteSystemService remoteSystemService;

    @Resource
    private RedisService redisService;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private final static long expireTime = CacheConstants.EXPIRATION;

    @Override
    public Map<String, String> createToken(User user, HttpServletResponse response) {
        String userLoginUniqueKey = IdUtils.fastUUID();
        String userId = user.getId();
        String username = user.getUsername();

        LoginUser loginUser = new LoginUser();
        loginUser.setUser(user);
        loginUser.setUserLoginUniqueKey(userLoginUniqueKey);
        loginUser.setUserid(userId);
        loginUser.setUsername(username);
        loginUser.setIpaddr(IpUtils.getIpAddr());
        //刷新token
        refreshToken(loginUser);
        Map<String, Object> claimsMap = new HashMap<>();
        claimsMap.put(SecurityConstants.USER_LOGIN_UNIQUE_KEY, userLoginUniqueKey);
        claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
        claimsMap.put(SecurityConstants.DETAILS_USERNAME, username);
        String access_token = jwtUtil.generateAccessToken(claimsMap, username);
        response.setHeader(JwtUtil.HEADER, access_token);
        response.setHeader("Access-control-Expost-Headers", JwtUtil.HEADER);
        Map<String, String> map = new HashMap<>();
        map.put("access_token", access_token);
        return map;
    }

    @Override
    public User getUserInfo(String username, String password) {
        AjaxResult ajaxResult = remoteSystemService.getUserByUsername(username);
        if (ajaxResult == null || ajaxResult.isError()) {
            throw new RuntimeException("远程查询用户出现异常");
        }
        User user = JSON.parseObject(JSON.toJSONString(ajaxResult.get("data")), User.class);
        if (user == null) {
            throw new RuntimeException("用户名不存在");
        }
        if (!BCryptUtil.validatePassword(password, user.getPassword())) {
            throw new RuntimeException("用户名或密码错误");
        }
        return user;
    }

    @Override
    public String getAccessToken(HttpServletRequest httpServletRequest) {
        return jwtUtil.getTokenReplacePrefix(httpServletRequest.getHeader(JwtUtil.HEADER));
    }

    /**
     * 用户登出
     */
    @Override
    public void logout(String accessToken) {
        String loginRequestToken = jwtUtil.getClaimFiled(accessToken, SecurityConstants.USER_LOGIN_UNIQUE_KEY);
        jwtUtil.logout(loginRequestToken);
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 获取loginUserKey
        String loginUserKey = jwtUtil.getLoginUserKey(loginUser.getUserLoginUniqueKey());
        redisService.setCacheObject(loginUserKey, loginUser, expireTime, TimeUnit.MINUTES);
    }
}
